// Author; Gonzalo Luzardo M.
// Contact: gonchalox@gmail.com, gluzardo@espol.edu.ec

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class XMLConfLoader {
	
	private DocumentBuilderFactory m_dbfact;
	private DocumentBuilder m_dbuild;
	private Document m_doc;
	private String m_file_name;
	
	//Parameters
	private int m_video_width;
	private int m_video_height;
	private int m_video_capture_period_time;
	private int m_face_watch_min_time;
	private String m_classifier_file;
	private MosaicStyle m_mosaic_style;
	private int m_mosaic_width;
	private int m_mosaic_height;
	private int m_mosaic_edge_pixels;
	private int m_mosaic_random_pixels;
	
	private boolean m_out_fullscreen;
	
	
	
	//Default parameters
	private int m_default_video_width=320;
	private int m_default_video_height=240;
	private int m_default_video_capture_period_time=1;
	private int m_default_face_watch_min_time=2;
	private String m_default_classifier_file="haarcascade_frontalface_alt2.xml";
	private MosaicStyle m_default_mosaic_style=MosaicStyle.DEFAULT;
	private int m_default_mosaic_width=320;
	private int m_default_mosaic_height=240;
	private int m_default_mosaic_edge_pixels=320;
	private int m_default_mosaic_random_pixels=500;
	private boolean m_default_out_fullscreen=true;
	
	//Constructor
	public XMLConfLoader(String file_name){
		m_file_name = file_name;
	}
	
	//Load XML
	public void parseXML() throws XMLConfLoaderException{
		m_dbfact = DocumentBuilderFactory.newInstance();
		try{
			m_dbuild = m_dbfact.newDocumentBuilder();
		}
		catch (ParserConfigurationException e_parse) {
			throw new XMLConfLoaderException(e_parse.getMessage());
		}
		try{
			m_doc = m_dbuild.parse (new File(m_file_name));
		} 
		catch (SAXException e_sax) {
			throw new XMLConfLoaderException(e_sax.getMessage());
		} 
		catch (IOException e_io) {
			throw new XMLConfLoaderException(e_io.getMessage());
		}
		
		// Normalize text representation
        m_doc.getDocumentElement().normalize ();      
        System.out.println ("Reading xml file, root node: " + m_doc.getDocumentElement().getNodeName());
        
        //Read video source parameters
        NodeList node;
        Element element;
        node = m_doc.getElementsByTagName("video_source");
        if(node.getLength()==0){
        	throw new XMLConfLoaderException("Video Source parameters not found");
        }
        else{
        	element = (Element) (((Element) node.item(0)).getElementsByTagName("width")).item(0);
        	if(element == null){
        		throw new XMLConfLoaderException("Cant read video width param");
        	}
        	m_video_width = Integer.parseInt(((element.getChildNodes()).item(0)).getNodeValue().trim());
        	if(m_video_width  <= 0){
        		throw new XMLConfLoaderException("Wrong value in video width param");
        	}
        	
        	element = (Element) (((Element) node.item(0)).getElementsByTagName("height")).item(0);
        	if(element == null){
        		throw new XMLConfLoaderException("Cant read video height param");
        	}
        	m_video_height = Integer.parseInt(((element.getChildNodes()).item(0)).getNodeValue().trim());
        	if(m_video_height  <= 0){
        		throw new XMLConfLoaderException("Wrong value in video height param");
        	}
        	
        	element = (Element) (((Element) node.item(0)).getElementsByTagName("capture_period_time")).item(0);
        	if(element == null){
        		throw new XMLConfLoaderException("Cant read video capture_period_time param");
        	}
        	m_video_capture_period_time = Integer.parseInt(((element.getChildNodes()).item(0)).getNodeValue().trim());
        	if(m_video_capture_period_time  <= 0){
        		throw new XMLConfLoaderException("Wrong value in video capture_period param");
        	}
        }

        //Read face detection parameters
        node = m_doc.getElementsByTagName("face_detection");
        if(node.getLength() ==0){
        	throw new XMLConfLoaderException("Face detection parameters  not found");
        }
        else{
        	element = (Element) (((Element) node.item(0)).getElementsByTagName("watch_min_time")).item(0);
        	if(element == null){
        		throw new XMLConfLoaderException("Cant read face detection watch_min_time param");
        	}
        	m_face_watch_min_time = Integer.parseInt(((element.getChildNodes()).item(0)).getNodeValue().trim());
        	if(m_face_watch_min_time  <= 0){
        		throw new XMLConfLoaderException("Wrong value in face detection watch_min_time param");
        	}
        	
        	element = (Element) (((Element) node.item(0)).getElementsByTagName("classifier_xml_file")).item(0);
        	if(element == null){
        		throw new XMLConfLoaderException("Cant read face detection classifier_xml_file param");
        	}
        	m_classifier_file = ((element.getChildNodes()).item(0)).getNodeValue().trim();
        	if(m_classifier_file.length()==0){
        		throw new XMLConfLoaderException("Wrong value in face detection classifier_xml_file param");
        	}
        }
        
        //Read Mosaic Parameters
        node = m_doc.getElementsByTagName("mosaic");
        if(node.getLength()==0){
        	throw new XMLConfLoaderException("Mosaic parameters not found");
        }
        else{
        	element = (Element) (((Element) node.item(0)).getElementsByTagName("style")).item(0);
        	if(element == null){
        		throw new XMLConfLoaderException("Cant read vitral style param");
        	}
        	m_mosaic_style = MosaicStyle.parseInt(Integer.parseInt(((element.getChildNodes()).item(0)).getNodeValue().trim()));
        	if(m_classifier_file.length()==0){
        		throw new XMLConfLoaderException("Wrong value in mosaic style param");
        	}
        	
        	element = (Element) (((Element) node.item(0)).getElementsByTagName("width")).item(0);
        	if(element == null){
        		throw new XMLConfLoaderException("Cant read vitral width param");
        	}
        	m_mosaic_width = Integer.parseInt(((element.getChildNodes()).item(0)).getNodeValue().trim());
        	if(m_classifier_file.length()==0){
        		throw new XMLConfLoaderException("Wrong value in mosaic width param");
        	}
        	
        	element = (Element) (((Element) node.item(0)).getElementsByTagName("height")).item(0);
        	if(element == null){
        		throw new XMLConfLoaderException("Cant read vitral height param");
        	}
        	m_mosaic_height = Integer.parseInt(((element.getChildNodes()).item(0)).getNodeValue().trim());
        	if(m_classifier_file.length()==0){
        		throw new XMLConfLoaderException("Wrong value in mosaic height param");
        	}
        	
        	element = (Element) (((Element) node.item(0)).getElementsByTagName("edge_pixels_offset")).item(0);
        	if(element == null){
        		throw new XMLConfLoaderException("Cant read face detection edge_pixels_offset param");
        	}
        	m_mosaic_edge_pixels = Integer.parseInt(((element.getChildNodes()).item(0)).getNodeValue().trim());
        	if(m_mosaic_edge_pixels  <= 0){
        		throw new XMLConfLoaderException("Wrong value in face detection edge_pixels_offset");
        	}
        	
        	element = (Element) (((Element) node.item(0)).getElementsByTagName("random_pixels")).item(0);
        	if(element == null){
        		throw new XMLConfLoaderException("Cant read face detection random_pixels param");
        	}
        	m_mosaic_random_pixels = Integer.parseInt(((element.getChildNodes()).item(0)).getNodeValue().trim());
        	if(m_mosaic_random_pixels  <= 0){
        		throw new XMLConfLoaderException("Wrong value in face detection random_pixels param");
        	}
        }
        
        //Read out parameters
        node = m_doc.getElementsByTagName("out");
        if(node.getLength()==0){
        	throw new XMLConfLoaderException("Out parameters not found");
        }
        else{
        	element = (Element) (((Element) node.item(0)).getElementsByTagName("fullscreen")).item(0);
        	if(element == null){
        		throw new XMLConfLoaderException("Cant read out fullscreen param");
        	}
        	
        	m_out_fullscreen = Boolean.valueOf(((element.getChildNodes()).item(0)).getNodeValue().trim());
        	if(m_classifier_file.length()==0){
        		throw new XMLConfLoaderException("Wrong value in out fullscreen param");
        	}
        }
                
        System.out.println("\nParams readed form XML File...");
        System.out.println("Video Width: " + m_video_width);
        System.out.println("Video Height: " + m_video_height);
        System.out.println("Video Capture period: " + m_video_capture_period_time);
        System.out.println("Face detection watch min time: " + m_face_watch_min_time);
        System.out.println("Face detection XML classifier file: " + m_classifier_file);
        System.out.println("Mosaic style: " + m_mosaic_style);        
        System.out.println("Mosaic width: " + m_mosaic_width);
        System.out.println("Mosaic height: " + m_mosaic_height);
        System.out.println("Mosaic edge pixels: " + m_mosaic_edge_pixels);
        System.out.println("Mosaic random pixels: " + m_mosaic_random_pixels);
        System.out.println("Out fullscreen: " + m_out_fullscreen);
	}
	
	//Set default parameters
	public void setDefaultParameters(){
		m_video_width=m_default_video_width;
		m_video_height=m_default_video_height;
		m_video_capture_period_time=m_default_video_capture_period_time;
		m_face_watch_min_time=m_default_face_watch_min_time;
		m_classifier_file=m_default_classifier_file;
		m_mosaic_style=m_default_mosaic_style;
		m_mosaic_width=m_default_mosaic_width;
		m_mosaic_height=m_default_mosaic_height;
		m_mosaic_edge_pixels = m_default_mosaic_edge_pixels;
		m_mosaic_random_pixels = m_default_mosaic_random_pixels;
		m_out_fullscreen=m_default_out_fullscreen;
	}
	
	public int getVideoWidth(){
		return m_video_width;
	}
	
	public int getVideoHeight(){
		return m_video_height;
	}
	
	public int getVideoCapturePeriodTime(){
		return m_video_capture_period_time;
	}
	
	public int getFaceWatchMinTime(){
		return m_face_watch_min_time;
	}
	
	public String getClassifierFile(){
		return m_classifier_file;
	}
	
	public MosaicStyle getMosaicStyle(){
		return m_mosaic_style;
	}
	
	public int getMosaicWidth(){
		return m_mosaic_width;
	}
	
	public int getMosaicHeight(){
		return m_mosaic_height;
	}
	
	public int getMosaicEdgePixels(){
		return m_mosaic_edge_pixels;
	}
	
	public int getMosaicRandomPixels(){
		return m_mosaic_random_pixels;
	}
}




//Class to control XML configuration errors
class XMLConfLoaderException extends Exception{

	private static final long serialVersionUID = 1L;

	public XMLConfLoaderException() {}

	//Constructor for exception description
	public XMLConfLoaderException(String description) {
	    super(description);
	}
}
